package com.djhu.springsimple.beans.context;

import com.djhu.springsimple.beans.core.ApplicationEvent;
import com.djhu.springsimple.beans.core.BeanFactoryAware;
import com.djhu.springsimple.beans.factory.BaseBeanFactory;
import com.djhu.springsimple.beans.factory.BeanFactory;

import java.util.List;
import java.util.concurrent.Executor;

/**
 * @author by zw on 2018/4/16.
 */
public class SimpleEventMulticaster implements ApplicationEventMulticaster,BeanFactoryAware{
    private BaseBeanFactory  baseBeanFactory;
    private Executor taskExecutor;
    private ErrorHandler errorHandler;

    public SimpleEventMulticaster(BaseBeanFactory baseBeanFactory) {
        this.baseBeanFactory = baseBeanFactory;
    }

    @Override
    public void multicastEvent(ApplicationEvent event) throws Exception {
        List<ApplicationListener> applicationListeners =  baseBeanFactory.getBeansForType(ApplicationListener.class);
        for (ApplicationListener applicationListener:applicationListeners){
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(applicationListener, event);
                    }
                });
            }
            else {
                invokeListener(applicationListener, event);
            }
        }
    }




    protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                listener.onApplicationEvent(event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            try {
                listener.onApplicationEvent(event);
            }
            catch (ClassCastException ex) {
                String msg = ex.getMessage();
                if (msg == null || msg.startsWith(event.getClass().getName())) {
                    // Possibly a lambda-defined listener which we could not resolve the generic event type for
                }
                else {
                    throw ex;
                }
            }
        }
    }

    public void setTaskExecutor(Executor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        baseBeanFactory = (BaseBeanFactory) beanFactory;
    }

    public Executor getTaskExecutor() {
        return taskExecutor;
    }

    public void setErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    public ErrorHandler getErrorHandler() {
        return errorHandler;
    }
}